package org.example.student_dormitory.utils;

import java.sql.*;
import java.util.*;

/**
 * 数据库工具类
 * 注意:一般情况下封装的工具类中都是一些常量和静态方法
 * 堆、栈、方法区
 */
public class DBUtils {
    /* 声明连接数据库必须的字符串常量 */
    public static final String DRIVER_CLASS = "com.mysql.cj.jdbc.Driver";
    public static final String URL = "jdbc:mysql://127.0.0.1:3306/stu_dormitory?useSSL=false&serverTimezone=Asia/Shanghai";
    public static final String USER = "root";
    public static final String PASSWORD = "111111";

    //静态块  static
    // 静态块与游离块的区别
    static{
        try {
            /*
            加载驱动:由于驱动只需要在第一次连接数据库
            之前加载一次即可，不需要反复执行
             */
            Class.forName(DRIVER_CLASS);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    //获取连接
    public static Connection getConn(){
        try {
            return  DriverManager.getConnection(URL,USER,PASSWORD);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static int insertGetKey(String sql,Object... params) throws SQLException {
        ResultSet rs = null;
        Connection conn = getConn();
        //获取sql预处理对象，可以获取自动生成的键
        PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        for (int i = 0; i < params.length; i++) {
            ps.setObject(i+1,params[i]);
        }
        //执行sql
        int i = ps.executeUpdate();
        if(i>0){
            //插入成功，获取自动生成的键
            rs = ps.getGeneratedKeys();
            rs.next();
            int id = rs.getInt(1);
            DBUtils.close(rs,ps,conn);
            return id;
        }else{
            DBUtils.close(rs,ps,conn);
            return -1;
        }

    }

    /**
     * 执行通用的更新操作(insert,update,delete)
     * insert into user(username,password) values(?,?)
     * update user set password=? where id=?
     * delete from user where id=?
     *
     * @param sql
     * @param params
     * @return
     */
    public static boolean exeUpdate(String sql,Object... params) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        try{
            conn = getConn();
            ps = conn.prepareStatement(sql);
            if(Objects.nonNull(params)){
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i+1,params[i]);
                }
            }
            return ps.executeUpdate()>0;
        }finally {
            close(null,ps,conn);
        }

    }

    /**
     * 执行通用的更新操作，外部传入连接对象(为事务手动提交提供支持)
     * @param conn  连接对象
     * @param sql  sql语句
     * @param params 参数
     * @return
     * @throws SQLException
     */
    public static boolean exeUpdate(Connection conn,String sql,Object... params) throws SQLException {
        PreparedStatement ps = null;
        try{
            //对sql做预处理，获取执行对象
            ps = conn.prepareStatement(sql);
            if(Objects.nonNull(params)){
                //获取所有的参数列表
                for (int i = 0; i < params.length; i++) {
                    //为每一个？填充具体的值
                    ps.setObject(i+1,params[i]);
                }
            }
            return ps.executeUpdate()>0;
        }finally {
            //只能关闭PreparedStatement，不能关闭连接
            close(null,ps,null);
        }

    }

    /**
     * 执行插入操作
     * @param sql
     * @param params
     * @return
     */
    public static boolean insert(String sql,Object... params) throws SQLException {
        return exeUpdate(sql,params);
    }

    /**
     * 执行插入操作，手动控制事务
     * @param conn 数据库连接对象
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public static boolean insert(Connection conn,String sql,Object... params) throws SQLException {
        return exeUpdate(conn,sql,params);
    }

    /**
     * 执行修改操作
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public static boolean update(String sql,Object... params) throws SQLException {
        return exeUpdate(sql,params);
    }

    /**
     * 执行修改操作，手动控制事务
     * @param conn 数据库连接
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public static boolean update(Connection conn,String sql,Object... params) throws SQLException {
        return exeUpdate(conn,sql,params);
    }

    /**
     * 执行删除操作
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public static boolean delete(String sql,Object... params) throws SQLException {
        return exeUpdate(sql,params);
    }

    /**
     * 删除操作，手动控制事务
     * @param conn
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public static boolean delete(Connection conn,String sql,Object... params) throws SQLException {
        return exeUpdate(conn,sql,params);
    }

    /**
     * 根据SQL语句实现对任何表任何列的查询(查询结果只有一条数据)
     * @param sql
     * @param params
     * @return
     */
    public static Map<String,Object> qureyOne(String sql, Object... params){
        List<Map<String, Object>> list = queryList(sql, params);
        if (list.isEmpty()){
            return null;
        }
        return list.get(0);
    }

    /**
     * 根据提供的SQL语句实现对任何表任何列的查询，并返回结果
     * select * from user where username=? and password=?
     * "admin","123456"
     * sql语句占位符索引是从1开始
     * 数组的索引是从0开始
     *
     * @param sql
     * @param params
     * @return
     */
    public static List<Map<String,Object>> queryList(String sql, Object... params){
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try{
            //获取连接
            conn = getConn();
            //对SQL语句做预处理
            ps = conn.prepareStatement(sql);
            //根据数组中元素的个数为sql中的占位符赋值
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i+1,params[i]);
            }
            //执行查询
            rs = ps.executeQuery();
            //将结果集转为一个List集合
            return queryToMapList(rs);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            close(rs,ps,conn);
        }
    }

    /**
     * 将一个ResultSet转换为List<Map<String,Object>>对象并返回
     * @param rs
     * @return
     * @throws SQLException
     */
    public static List<Map<String,Object>> queryToMapList(ResultSet rs) throws SQLException {
        List<Map<String,Object>> list = new ArrayList<>();
        //获取结果集元数据
        ResultSetMetaData metaData = rs.getMetaData();
        //通过元数据获取查询列的总数
        int columnCount = metaData.getColumnCount();
        while (rs.next()){
            //结果集中的每一行数据都可以表示为一个Map对象(Java Bean)
            Map<String,Object> lineMap = new HashMap<>();
            for (int i = 1; i <= columnCount ; i++) {
                //获取当前列标签名称
                String key = metaData.getColumnLabel(i);
                //获取当前列的值
                Object value = rs.getObject(key);
                //将一个列的名称作为键，对应的值作为值存储到Map中
                lineMap.put(key,value);
            }
            //将一行数据的map装入list集合
            list.add(lineMap);
        }
        return list;
    }

    /**
     * 关闭资源
     * @param rs ResultSet 结果集
     * @param stat Statement对象
     * @param conn Connection对象
     */
    public static void close(ResultSet rs, Statement stat, Connection conn){
        //选中要捕获异常的代码块儿，按Ctrl+Alt+T  选择try/chtch
        try {
            if (rs != null){
                rs.close();
            }
            if(stat != null){
                stat.close();
            }
            if(Objects.nonNull(conn)){
                conn.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

    }

}
